using System;
using System.Collections.Generic;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace HadronData
{
    public class PickedVertex
    {
        private Layer m_layer;
        private Edge m_edge;
        private bool m_bStartVertex;

        public PickedVertex(Layer p_layer, Edge p_edge, bool p_bStartVertex)
        {
            m_layer = p_layer;
            m_edge = p_edge;
            m_bStartVertex = p_bStartVertex;
        }

        public Layer Layer
        {
            get { return m_layer; }
            set { m_layer = value; }
        }

        public Edge Edge
        {
            get { return m_edge; }
            set { m_edge = value; }
        }

        public bool StartVertex
        {
            get { return m_bStartVertex; }
            set { m_bStartVertex = value; }
        }

        public Vector2 Vertex
        {
            get { return m_bStartVertex ? m_layer.Placement.Apply(m_edge.Start) : m_layer.Placement.Apply(m_edge.End); }
            set
            {
                Vector2 vecValueLocal = m_layer.Placement.Inverse.Apply(value);
                if (m_bStartVertex)
                    m_edge.Start = vecValueLocal;
                else
                    m_edge.End = vecValueLocal;
            }
        }
    }

    public class PickedEdge
    {
        private Layer m_layer;
        private Edge m_edge;

        public PickedEdge(Layer p_layer, Edge p_edge)
        {
            m_layer = p_layer;
            m_edge = p_edge;
        }

        public Layer Layer
        {
            get { return m_layer; }
            set { m_layer = value; }
        }

        public Edge Edge
        {
            get
            {
                Edge edgeAbsolute = new Edge(m_edge);
                edgeAbsolute.Transform(m_layer.Placement);
                return edgeAbsolute;
            }
            set
            {
                Edge edgeLocal = new Edge(value);
                edgeLocal.Transform(m_layer.Placement.Inverse);
                m_edge = edgeLocal;
            }
        }

        public Edge LocalEdge
        {
            get { return m_edge; }
            set { m_edge = value; }
        }
    }

    public class Level
    {
        private String m_strName;
        private String[] m_arrayMission;
        private Vector2 m_vecDimensions;
        private List<Layer> m_listLayers;
        private List<EntityMarker> m_listEntityMarkers;
        private LevelObjectives m_levelObjectives;

        public Level()
        {
            m_strName = "Untitled Level";
            m_arrayMission = new String[0];
            m_vecDimensions = new Vector2(2048.0f, 1024.0f);
            m_listLayers = new List<Layer>();
            m_listEntityMarkers = new List<EntityMarker>();
            m_levelObjectives = new LevelObjectives();
        }

        public Layer GetLayer(String p_strLayerName)
        {
            foreach (Layer layer in m_listLayers)
                if (layer.Name == p_strLayerName)
                    return layer;
            return null;
        }

        public EntityMarker GetEntityMarker(String p_strEntityMarkerName)
        {
            foreach (EntityMarker entityMarker in m_listEntityMarkers)
                if (entityMarker.Name == p_strEntityMarkerName)
                    return entityMarker;
            return null;
        }

        public bool Intersects(Edge p_edge)
        {
            foreach (Layer layer in m_listLayers)
                if (layer.Intersects(p_edge))
                    return true;
            return false;
        }

        public bool Intersects(Ray p_ray)
        {
            foreach (Layer layer in m_listLayers)
                if (layer.Intersects(p_ray))
                    return true;
            return false;
        }

        public bool Intersects(Ray p_ray, ref Vector2 p_vecIntersectionPoint)
        {
            float fShortestDistance = float.MaxValue;
            Vector2 vecIntersctionPoint = Vector2.Zero;
            bool bIntersection = false;

            foreach (Layer layer in m_listLayers)
            {
                if (!layer.Intersects(p_ray, ref vecIntersctionPoint))
                    continue;

                float fDistance = p_ray.GetSignedTangentDistance(vecIntersctionPoint);
                if (fShortestDistance > fDistance)
                {
                    p_vecIntersectionPoint = vecIntersctionPoint;
                    fShortestDistance = fDistance;
                }

                bIntersection = true;
            }

            return bIntersection;
        }

        public bool Intersects(Circle p_circle)
        {
            foreach (Layer layer in m_listLayers)
                if (layer.Intersects(p_circle))
                    return true;
            return false;
        }

        public bool Intersects(Polygon p_polygon)
        {
            foreach (Layer layer in m_listLayers)
                if (layer.Intersects(p_polygon))
                    return true;
            return false;
        }

        public void ComputeContactPoints(Edge p_edge, List<ContactPoint> p_listContactPoints)
        {
            foreach (Layer layer in m_listLayers)
                layer.ComputeContactPoints(p_edge, p_listContactPoints);
        }

        public void ComputeContactPoints(Circle p_circle, List<ContactPoint> p_listContactPoints)
        {
            foreach (Layer layer in m_listLayers)
                layer.ComputeContactPoints(p_circle, p_listContactPoints);
        }

        public void ComputeContactPoints(Polygon p_polygon, List<ContactPoint> p_listContactPoints)
        {
            foreach (Layer layer in m_listLayers)
                layer.ComputeContactPoints(p_polygon, p_listContactPoints);
        }

        public void ComputeContactPoints(Geometry p_geometry, List<ContactPoint> p_listContactPoints)
        {
            foreach (Layer layer in m_listLayers)
                layer.ComputeContactPoints(p_geometry, p_listContactPoints);
        }

        public List<PickedVertex> PickVertices(Vector2 p_vecPosition)
        {
            List<PickedVertex> listPickedVertices = new List<PickedVertex>();
            foreach (Layer layer in m_listLayers)
            {
                Vector2 vecPositionLocal = layer.Placement.Inverse.Apply(p_vecPosition);
                foreach (Edge edge in layer.Edges)
                {
                    if ((vecPositionLocal - edge.End).LengthSquared() < 100.0f)
                        listPickedVertices.Add(new PickedVertex(layer, edge, false));
                    else if ((vecPositionLocal - edge.Start).LengthSquared() < 100.0f)
                        listPickedVertices.Add(new PickedVertex(layer, edge, true));
                }
            }

            return listPickedVertices;
        }

        public List<PickedEdge> PickEdges(Vector2 p_vecPosition)
        {
            List<PickedEdge> listPickedEdges = new List<PickedEdge>();
            foreach (Layer layer in m_listLayers)
            {
                Vector2 vecPositionLocal = layer.Placement.Inverse.Apply(p_vecPosition);
                foreach (Edge edge in layer.Edges)
                {
                    if ((vecPositionLocal - edge.GetClosestPoint(vecPositionLocal)).LengthSquared() < 100.0f)
                        listPickedEdges.Add(new PickedEdge(layer, edge));
                }
            }

            return listPickedEdges;
        }

        public List<EntityMarker> PickEntityMarkers(Vector2 p_vecPosition)
        {
            List<EntityMarker> listEntityMarkers = new List<EntityMarker>();
            foreach (EntityMarker entityMarker in m_listEntityMarkers)
                if ((p_vecPosition - entityMarker.Position).LengthSquared() < 900.0f)
                    listEntityMarkers.Add(entityMarker);

            return listEntityMarkers;
        }

        public EntityMarker PickEntityMarker(Vector2 p_vecPosition)
        {
            foreach (EntityMarker entityMarker in m_listEntityMarkers)
                if ((p_vecPosition - entityMarker.Position).LengthSquared() < 900.0f)
                    return entityMarker;

            return null;
        }

        public void Update(GameTime p_gameTime)
        {
            foreach (Layer layer in m_listLayers)
                layer.Update(p_gameTime);
        }

        public void Draw(SpriteBatch p_spriteBatch)
        {
            foreach (Layer layer in m_listLayers)
                layer.Draw(p_spriteBatch);
        }

        public String Name
        {
            get { return m_strName; }
            set { m_strName = value; }
        }

        public String[] Mission
        {
            get { return m_arrayMission; }
            set { m_arrayMission = value; }
        }

        public Vector2 Dimensions
        {
            get { return m_vecDimensions; }
            set { m_vecDimensions = value; }
        }

        public List<Layer> Layers
        {
            get { return m_listLayers; }
            set { m_listLayers = value; }
        }

        public List<EntityMarker> EntityMarkers
        {
            get { return m_listEntityMarkers; }
            set { m_listEntityMarkers = value; }
        }

        public LevelObjectives Objectives
        {
            get { return m_levelObjectives; }
            set { m_levelObjectives = value; }
        }
    }
}
